#!/usr/bin/python3
from git.repo import Repo
import os
import subprocess
import sys
import json
import xml.etree.ElementTree as ET
import glob
import pom_change
from git.repo.fun import is_git_dir


class Pom:
    def __init__(self, group_id, artifact_id):
        self.__group_id = group_id
        self.__artifact_id = artifact_id

    def __repr__(self):
        return "<groupId>{}</groupId>\n<artifactId>{}</artifactId>".format(
            self.__group_id, self.__artifact_id
        )

    def __str__(self):
        return "{}->{}".format(self.__artifact_id, self.__group_id)

    def __eq__(self, other):
        if (
            self.__group_id == other.__group_id
            and self.__artifact_id == other.__artifact_id
        ):
            return True
        else:
            return False

    def __hash__(self):
        return hash("{}---{}".format(self.__group_id, self.__artifact_id))


def get_pom_list(base_path):
    projects = os.listdir(base_path)
    pom_files = []
    for proj in projects:
        project_file = os.path.join(base_path, proj)
        for home, dir, files in os.walk(os.path.join(base_path, proj)):
            for file in files:
                if file.endswith("pom.xml"):
                    pom_files.append(os.path.join(home, file))
        root_pom_file = os.path.join(project_file, "pom.xml")
        if os.path.exists(root_pom_file) and root_pom_file not in pom_files:
            pom_files.append(root_pom_file)
        if (
            os.path.isfile(project_file)
            and project_file.endswith("pom.xml")
            and project_file not in pom_files
        ):
            pom_files.append(project_file)
    return pom_files


def get_project_info(base_path):
    pom_list = []
    pom_files = get_pom_list(base_path)
    for pom_file in pom_files:
        pom_xml_file = open(pom_file, "tr", encoding="utf-8")
        print(pom_file)
        pom_tree = ET.parse(pom_xml_file)
        pom_root = pom_tree.getroot()
        default_namespacetom = pom_root.tag.split("}")[0][1:]
        ns = {}
        ns["tomns"] = default_namespacetom
        item = get_self_pom(pom_tree, ns)
        pom_list.append(str(item))
    return pom_list


def get_self_pom(pom_tree, ns):
    selfGroupId = pom_tree.find(".tomns:groupId", ns)
    if selfGroupId is None:
        selfGroupId = pom_tree.find(".tomns:parent/tomns:groupId", ns)
        if selfGroupId is None:
            print("your pom.xml not found groupId , please check")
            return None
    selfArtifactId = pom_tree.find(".tomns:artifactId", ns)
    item = Pom(selfGroupId.text, selfArtifactId.text)
    return item


def updateDepencyVersion(
    install_project, path_result, self_item, item, update, version
):
    if "application" not in item and "org.thingsboard" in item:
        return -1
    # 欲修改文件
    xml_path = path_result[self_item]
    itemObj = getPomByPathObj(path_result[item])
    # 修改文件中的xpath定位
    group_id = itemObj.__getattribute__("_Pom__group_id")
    artifact_id = itemObj.__getattribute__("_Pom__artifact_id")
    # 想要修改成什么内容
    update_content = version
    version = pom_change.change_one_xml(
        path_result,
        install_project,
        xml_path,
        group_id,
        artifact_id,
        update,
        update_content,
    )
    print("1depency..{}..{}..{}".format(self_item, item, version))
    return version


def updatePomVersion(install_project, path_result, self_item, update, version):
    xml_path = path_result[self_item]
    if self_item in install_project:
        return install_project[self_item]
    version = pom_change.change_self_version(
        path_result, install_project, xml_path, update, version
    )
    install_project[self_item] = version
    print("1..self_item={}..version={}".format(self_item, version))
    tree = ET.parse(xml_path)
    root = tree.getroot()
    location_name = root.tag[: root.tag.index("}") + 1]
    parentNode = root.find("." + location_name + "parent")
    if parentNode is not None:
        # update parent pom content
        groupIdNode = parentNode.find("." + location_name + "groupId")
        artifactIdNode = parentNode.find("." + location_name + "artifactId")
        item = Pom(groupIdNode.text, artifactIdNode.text)
        if str(item) in path_result:
            updatePomVersion(install_project, path_result, str(item), True, version)

    # res = subprocess.getoutput("mvn clean install -Pzxhtom -DskipTests -f {}".format(xml_path))
    # if "[INFO] BUILD SUCCESS" in res:
    #     print("start push code....{}".format(xml_path))
    #     subprocess.getoutput("cd {} && git push".format(os.path.dirname(xml_path)))
    return version


def is_git_dir(path):
    listFiles = os.listdir(path)
    if ".git" in listFiles:
        return True
    return False


def get_project_ele_info(branch_json, path_result, self_item):
    json = {}
    if self_item in path_result:
        parentPath = os.path.dirname(path_result[self_item])
        while is_git_dir(parentPath) is False:
            parentPath = os.path.dirname(parentPath)
        json["repo_path"] = parentPath
        print("do start update {}".format(parentPath))
        exit_repo = Repo(json["repo_path"])
        exit_repo.remote().fetch()
        # try:
        #     exit_repo.remote().pull()
        # except Exception as e:
        #     print(json["repo_path"])
        #     print(e)
        if exit_repo.is_dirty():
            exit_repo.git.add(".")
            # exit_repo.git.commit("-m", "stage local files")
        json["source_branch"] = exit_repo.active_branch.name
        parentPath = subprocess.getoutput(
            "cd " + parentPath + " && git remote -v | sed -n '1p' | awk '{print $2}'"
        )
        if (parentPath in branch_json) and ("main_branch" in branch_json[parentPath]):
            json["target_branch"] = branch_json[parentPath]["main_branch"]
        else:
            json["target_branch"] = "origin/{}".format(json["source_branch"])
        if json["source_branch"] == json["target_branch"]:
            json["target_branch"] = "origin/{}".format(json["source_branch"])
    return json


def remote_deploy(
    branch_json,
    gited,
    exclude,
    self_item,
    last_json_result,
    push=True,
    inTimes=0,
    ForceMerge=False,
):
    json_result = last_json_result["json_result"]
    path_result = last_json_result["path_result"]
    dependency_result = last_json_result["dependency_result"]
    self_json = get_project_ele_info(branch_json, path_result, self_item)
    repo_path = self_json["repo_path"]
    source_branch = self_json["source_branch"]
    target_branch = self_json["target_branch"]
    if self_item not in path_result:
        return
    xml_path = path_result[self_item]
    root_folder = os.path.dirname(xml_path)
    while is_git_dir(root_folder) is False:
        root_folder = os.path.dirname(root_folder)
    root_xml = os.path.join(root_folder, "pom.xml")
    if root_xml in gited:
        return
    pom_list = get_pom_list(root_folder)
    print("---------------------------")
    print(xml_path)
    for pomItem in pom_list:
        # if "shuiliandong-interface" in pomItem:
        #     continue
        selfItem = str(getPomByPath(pomItem))
        if selfItem in json_result:
            json_arr = json_result[selfItem]
            for item in json_arr:
                # if "shuiliandong-interface" in item:
                #     continue
                sub_xml_path = path_result[item]
                sub_root_folder = os.path.dirname(sub_xml_path)
                while is_git_dir(sub_root_folder) is False:
                    sub_root_folder = os.path.dirname(sub_root_folder)
                if root_folder == sub_root_folder:
                    continue
                inTimes = inTimes + 1
                remote_deploy(
                    branch_json,
                    gited,
                    exclude,
                    item,
                    last_json_result,
                    push,
                    inTimes,
                    ForceMerge,
                )
                inTimes = 0
    # push = root_folder == os.path.dirname(xml_path)
    if push is True:
        repo = Repo(root_folder)
        pushed_success = subprocess.getoutput(
            "cd {} && git log origin/{}..{}".format(
                root_folder, source_branch, source_branch
            )
        )
        if repo.is_dirty() or (len(pushed_success) > 0):
            maven_info = subprocess.getoutput("mvn --version")
            print("maven start install {}--------{}".format(xml_path, maven_info))
            addtional_command = ""
            if inTimes > 0 or self_item in dependency_result:
                addtional_command = "deploy"
                print(
                    "maven need others command such as {}...................".format(
                        addtional_command
                    )
                )
            if "yujingshan" not in root_xml:
                check_res = subprocess.getoutput(
                    "sh {}/mvnci.sh {} {} {}".format(
                        os.path.dirname(sys.argv[0]), root_xml, "com.yapai", exclude
                    )
                )
                if len(check_res) > 0:
                    if len(exclude) == 0:
                        exclude = check_res
                    else:
                        exclude = exclude + "," + check_res

            # if "deploy" in addtional_command:
            #     root_pom_list = get_pom_list(root_folder)
            #     for rootPom in root_pom_list:
            #         rootPomItem = getPomByPath(rootPom)
            #         root_group_id = rootPomItem.__getattribute__("_Pom__group_id")
            #         root_artifact_id = rootPomItem.__getattribute__("_Pom__artifact_id")
            #         subprocess.getoutput("mvn dependency:purge-local-repository -Dincludes={} -DreResolve=false -DactTransitively=false -f {}".format("{}:{}".format(root_group_id,root_artifact_id),rootPom))

            res = subprocess.getoutput(
                " mvn clean install {} -Pzxhtom -DskipTests -f {}".format(
                    addtional_command,
                    root_xml,
                )
            )
            if "[INFO] BUILD SUCCESS" in res:
                gited.append(root_xml)
                print("\033[32mstart push code....{}\033[0m".format(root_xml))
                subprocess.getoutput(
                    "cd {} &&git add . && git commit -m 'stage local changes' && git push".format(
                        os.path.dirname(root_xml)
                    )
                )
                subprocess.getoutput(
                    "cd {} && git push".format(os.path.dirname(root_xml))
                )
                if ForceMerge is True:
                    __merge(repo, source_branch, target_branch)
            else:
                print("------------" + root_folder + "---------------")
                print(res)


def __merge(repo, from_branch, to_branch):
    if from_branch != to_branch and to_branch != "origin/{}".format(from_branch):
        git = repo.git
        # 切换到源分支，并同步远程仓库的代码
        git.checkout(from_branch)
        git.pull()

        # 切换到目标分支，并同步远程仓库的代码
        git.checkout(to_branch)
        git.pull()

        # 将源分支的代码合并到目标分支，并将目标分支的最新代码推送到远程仓库
        git.merge(from_branch)
        git.push()

        git.checkout(from_branch)


def update_version(
    install_project,
    branch_json,
    updated_json,
    last_json_result,
    self_item,
    update,
    version,
):
    # --------------special area exclude start--------------
    if "application" not in self_item and "org.thingsboard" in self_item:
        return -1
    # --------------special area exclude end----------------
    if self_item in updated_json:
        return updated_json[self_item]
    json_result = last_json_result["json_result"]
    path_result = last_json_result["path_result"]
    if self_item in json_result:
        json_arr = json_result[self_item]
        for item in json_arr:
            new_version = update_version(
                install_project,
                branch_json,
                updated_json,
                last_json_result,
                item,
                update,
                version,
            )
            if -1 != new_version or ("" != new_version and update is True):
                # write to self_item
                updateDepencyVersion(
                    install_project, path_result, self_item, item, True, new_version
                )
    self_json = get_project_ele_info(branch_json, path_result, self_item)
    print("-------")
    print(self_json)
    repo_path = self_json["repo_path"]
    source_branch = self_json["source_branch"]
    target_branch = self_json["target_branch"]
    self_item_in_path = os.path.dirname(path_result[self_item])
    if repo_path != self_item_in_path:
        self_item_in_path = os.path.basename(self_item_in_path)
    result = check_access_of_update_version(
        repo_path, self_item_in_path, source_branch, target_branch
    )
    if (result is True) or (update is True and version != ""):
        # Update Version
        version = updatePomVersion(
            install_project, path_result, self_item, update, version
        )
        updated_json[self_item] = version
        return version
    else:
        updated_json[self_item] = -1
        return -1


def getPomByPath(pom_file):
    self_item = str(getPomByPathObj(pom_file))
    return self_item


def getPomByPathObj(pom_file):
    if os.path.exists(pom_file) is False:
        return None
    pom_xml_file = open(pom_file, "tr", encoding="utf-8")
    pom_tree = ET.parse(pom_xml_file)
    pom_root = pom_tree.getroot()
    default_namespacetom = pom_root.tag.split("}")[0][1:]
    ns = {}
    ns["tomns"] = default_namespacetom
    return get_self_pom(pom_tree, ns)


def analyze_dependency(base_path):
    last_json_result = {}
    json_result = {}
    path_result = {}
    dependency_result = []
    pom_files = get_pom_list(base_path)
    item_pom_list = get_project_info(base_path)
    for pom_file in pom_files:
        pom_xml_file = open(pom_file, "tr", encoding="utf-8")
        pom_tree = ET.parse(pom_xml_file)
        pom_root = pom_tree.getroot()
        default_namespacetom = pom_root.tag.split("}")[0][1:]
        ns = {}
        ns["tomns"] = default_namespacetom
        self_item = str(get_self_pom(pom_tree, ns))
        path_result[self_item] = pom_file
        for den in pom_tree.findall(".//tomns:dependency", ns):
            groupId = den.find(".tomns:groupId", ns)
            artifactId = den.find(".tomns:artifactId", ns)
            item = Pom(groupId.text, artifactId.text)
            if str(item) in item_pom_list:
                dependency_result.append(str(item))
                if self_item in json_result:
                    json_arr = json_result[str(self_item)]
                    json_arr.append(str(item))
                    json_result[str(self_item)] = json_arr
                else:
                    json_arr = []
                    json_arr.append(str(item))
                    json_result[str(self_item)] = json_arr

    last_json_result["json_result"] = json_result
    last_json_result["path_result"] = path_result
    last_json_result["dependency_result"] = dependency_result
    return last_json_result


def get_project_install(result):
    install_result = []
    json_result = json.loads(str(result).replace("'", '"'))
    print(json_result)
    install_result = []
    for key in json_result:
        install_result = install_projects(result, key, install_result)
    return install_result


def install_projects(result, key, install_result):
    # install_result = []
    print("-------------" + key)
    json_result = json.loads(str(result).replace("'", '"'))
    print(json_result)
    value = json_result[key]
    if len(value) == 0:
        install_result.append(key)
    else:
        for item in value:
            if item in install_result:
                continue
            if item in json_result and len(json_result[item]) == 0:
                install_result.append(item)
            else:
                install_result.extend(
                    install_projects(json_result, item, install_result)
                )

    return install_result


def merge_branch(repo_path, source_branch, target_branch, push=False):
    repo = Repo(repo_path)
    git = repo.git
    git.checkout(target_branch)
    git.pull("--progress", "--no-rebase", "origin", target_branch)
    git.checkout(source_branch)
    git.pull("--progress", "--no-rebase", "origin", source_branch)
    git.merge(target_branch)
    conflict_files = repo.git.execute(
        ["git", "diff", "--name-only", "--diff-filter=U"]
    )  # noqa
    if len(conflict_files) > 0:
        return False
    if push is True:
        git.push("--progress", "origin", source_branch)


def check_access_of_update_version(repo_path, self_path, source_branch, target_branch):
    print("repo_path={}..self_path={}".format(repo_path, self_path))
    if repo_path == self_path:
        self_path = " "
    if target_branch.startswith("origin"):
        # compare remote branch
        fetct_result = subprocess.getoutput(
            "cd {} && git fetch {} {}".format(
                repo_path, target_branch.split("/")[0], target_branch[7:]
            )
        )
        print("fetch result =={}".format(fetct_result))
    else:
        fetct_result = subprocess.getoutput(
            "cd {} && git fetch origin {}".format(repo_path, target_branch)
        )
        print("fetch result =={}".format(fetct_result))

    subprocess.getoutput(
        "cd {} && git fetch origin {}".format(repo_path, source_branch)
    )
    # back
    if self_path == " ":
        result = subprocess.getoutput(
            "cd {} && git diff-tree {} {} --stat".format(
                repo_path, source_branch, target_branch, self_path
            )
        )
    else:
        result = subprocess.getoutput(
            "cd {} && git diff-tree {} {} --stat ".format(
                repo_path, source_branch, target_branch, self_path
            )
        )
    repo = Repo(repo_path)
    if (repo.is_dirty() is False) and (len(result) == 0):
        print("{} and {} do not found difference".format(source_branch, target_branch))
        return False
    diff_log_cmd = "cd {} && git log {}..{} -n 1 | grep -5 -E 'update|更新版本' | grep -n 'commit' | head -n 1 | awk -F '[: ]+' '{{print $3}}'".format(
        repo_path, source_branch, target_branch
    )
    log_result = subprocess.getoutput(diff_log_cmd)
    file_result = ""
    if len(log_result) > 0:
        file_result = subprocess.getoutput(
            "cd {} && git show {} | grep '^+' | grep -E '<version>.*</version>'".format(
                repo_path, log_result
            )
        )
    if len(file_result) == 0:
        print(
            "we need update version {}..{}..{}..{}".format(
                repo_path, self_path, source_branch, target_branch
            )
        )
        subprocess.getoutput(
            "cd {} && git pull origin {}".format(repo_path, source_branch)
        )
        subprocess.getoutput("cd {} && git merge {}".format(repo_path, target_branch))
        return True
    return False


def check_main_branch_project_right_run(base_path, branch_json):
    success_install_project = []
    for remote_url in branch_json:
        project_name = remote_url.split("/")[-1].split(".")[0]
        branch_name = branch_json[remote_url]["main_branch"]
        project_folder = os.path.join(base_path, project_name)
        if os.path.exists(project_folder) is False:
            result = subprocess.Popen(
                "git clone -b {} {} {}".format(branch_name, remote_url, project_folder),
                shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
            )
            for next_line in result.stdout:
                print(next_line.decode("utf-8", "ignore"), end="")
        if os.path.exists(project_folder) is False:
            continue
        repo = Repo(project_folder)
        repo.git.pull()
        pom_file = os.path.join(project_folder, "pom.xml")
        if os.path.exists(pom_file):
            install_result = subprocess.Popen(
                "mvn clean install -DskipTests -Pzxhtom -Dmaven.repo.local={} -f {}".format(
                    os.path.join(os.path.expanduser("~"), "temp/repository"),
                    pom_file,
                ),
                shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
            )
            for next_line in install_result.stdout:
                temp_result = next_line.decode("utf-8", "ignore")
                print(temp_result, end="")
                if "[INFO] BUILD SUCCESS" in temp_result:
                    success_install_project.append(project_folder)
        print("----------------------")
        print("check folder success install project under......")
    for sp in success_install_project:
        print("\033[32m{}\033[0m".format(sp))
